#include <stdio.h>
#include <string.h>

typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned long u32;

typedef signed char s8;
typedef signed short s16;
typedef signed long s32;

#pragma pack(1)
typedef struct
{
	u16	mod_sig;		//should be "SM"
	u8	mod_len;		//song length (in patterns)
	u8	mod_patterns;	//number of patterns
	u8	mod_channels;	//number of channels, 4, 6 or 8
	u8	mod_samples;	//number of samples, 15 or 31
}TModHeader;

typedef struct  
{
	u32 sample_offset;			//position in sample data
	u16	sample_len;				//length of sample (in words)
	u16	sample_repeatOffset;	//where to start repeating (in words)
	u16	sample_repeatLen;		//how much to repeat (in words)
	u8	sample_volume;			//volume of sample, 0..64
	u8	sample_finetune;		//fine-tuning
}TModSample;

typedef struct 
{
	u16	pat_sampleID:5;			//sample ID
	u16	pat_sampleNote:7;		//index of the note from amiga table
	u16	pat_effect:4;			//effect number
	u8	pat_paramX;				//effect param x	stupid ARM data alignment, can't declare paramX:4 and paramY:4
	u8	pat_paramY;				//effect param y
}TModPattern;
#pragma pack()


TModHeader mod_header;
TModSample mod_samples[31];
TModPattern *mod_patterns;
s8 *mod_sampleData[31];
u8 mod_order[128];


u16 amiga_table[16][36] =
{
//	C   C#  D   D#  E   F   F#  G   G#  A   A#  B
// Tuning 0, Normal
{
	856,808,762,720,678,640,604,570,538,508,480,453,	//octave 1
	428,404,381,360,339,320,302,285,269,254,240,226,	//octave 2
	214,202,190,180,170,160,151,143,135,127,120,113		//octave 3
},
// Tuning 1
{
	850,802,757,715,674,637,601,567,535,505,477,450,
	425,401,379,357,337,318,300,284,268,253,239,225,
	213,201,189,179,169,159,150,142,134,126,119,113
},
// Tuning 2
{
	844,796,752,709,670,632,597,563,532,502,474,447,
	422,398,376,355,335,316,298,282,266,251,237,224,
	211,199,188,177,167,158,149,141,133,125,118,112
},
// Tuning 3
{
	838,791,746,704,665,628,592,559,528,498,470,444,
	419,395,373,352,332,314,296,280,264,249,235,222,
	209,198,187,176,166,157,148,140,132,125,118,111
},
// Tuning 4
{
	832,785,741,699,660,623,588,555,524,495,467,441,
	416,392,370,350,330,312,294,278,262,247,233,220,
	208,196,185,175,165,156,147,139,131,124,117,110
},
// Tuning 5
{
	826,779,736,694,655,619,584,551,520,491,463,437,
	413,390,368,347,328,309,292,276,260,245,232,219,
	206,195,184,174,164,155,146,138,130,123,116,109
},
// Tuning 6
{
	820,774,730,689,651,614,580,547,516,487,460,434,
	410,387,365,345,325,307,290,274,258,244,230,217,
	205,193,183,172,163,154,145,137,129,122,115,109
},
// Tuning 7
{
	814,768,725,684,646,610,575,543,513,484,457,431,
	407,384,363,342,323,305,288,272,256,242,228,216,
	204,192,181,171,161,152,144,136,128,121,114,108
},
// Tuning -8
{
	907,856,808,762,720,678,640,604,570,538,508,480,
	453,428,404,381,360,339,320,302,285,269,254,240,
	226,214,202,190,180,170,160,151,143,135,127,120
},
// Tuning -7
{
	900,850,802,757,715,675,636,601,567,535,505,477,
	450,425,401,379,357,337,318,300,284,268,253,238,
	225,212,200,189,179,169,159,150,142,134,126,119
},
// Tuning -6
{
	894,844,796,752,709,670,632,597,563,532,502,474,
	447,422,398,376,355,335,316,298,282,266,251,237,
	223,211,199,188,177,167,158,149,141,133,125,118
},
// Tuning -5
{
	887,838,791,746,704,665,628,592,559,528,498,470,
	444,419,395,373,352,332,314,296,280,264,249,235,
	222,209,198,187,176,166,157,148,140,132,125,118
},
// Tuning -4
{
	881,832,785,741,699,660,623,588,555,524,494,467,
	441,416,392,370,350,330,312,294,278,262,247,233,
	220,208,196,185,175,165,156,147,139,131,123,117
},
// Tuning -3
{
	875,826,779,736,694,655,619,584,551,520,491,463,
	437,413,390,368,347,328,309,292,276,260,245,232,
	219,206,195,184,174,164,155,146,138,130,123,116
},
// Tuning -2
{
	868,820,774,730,689,651,614,580,547,516,487,460,
	434,410,387,365,345,325,307,290,274,258,244,230,
	217,205,193,183,172,163,154,145,137,129,122,115
},
// Tuning -1
{
	862,814,768,725,684,646,610,575,543,513,484,457,
	431,407,384,363,342,323,305,288,272,256,242,228,
	216,203,192,181,171,161,152,144,136,128,121,114
}
};

bool loadMod(char *fname)
{
	u8 tmp[131072]; //max for 8 ch * 4 bytes * 64 notes * 64 patterns

	mod_header.mod_sig = 'S' + 'M' * 256 ;

	FILE *f = fopen(fname, "rb");
	if (!f)
		return false;

	fseek(f, 1080, SEEK_SET);
	fread(tmp, 4, 1, f);
	tmp[4] = 0;

	mod_header.mod_channels = 4;
	mod_header.mod_samples = 15;

	if (!strcmp((char*)tmp, "M.K."))
	{
		mod_header.mod_channels = 4;
		mod_header.mod_samples = 31;
	}

	if (!strcmp((char*)tmp, "FLT4"))
	{
		mod_header.mod_channels = 4;
		mod_header.mod_samples = 31;
	}

	if (!strcmp((char*)tmp, "6CHN"))
	{
		mod_header.mod_channels = 6;
		mod_header.mod_samples = 31;
	}

	if (!strcmp((char*)tmp, "FLT8") || !strcmp((char*)tmp, "8CHN"))
	{
		mod_header.mod_channels = 8;
		mod_header.mod_samples = 31;
	}

	if ((mod_header.mod_channels == 4)&&(mod_header.mod_samples == 15))
	{
		printf("Unknown format \"%s\". Assuming 4 channels, 15 samples.\n");
	}


	fseek(f, 0, SEEK_SET);

	//read song name
	fread(tmp, 20, 1, f);

	u32 offset = 0;
	for (int i=0;i<31;i++)
	{
		//read each sample data
		fread(tmp, 30, 1, f);
		mod_samples[i].sample_len = (tmp[22] << 8) + tmp[23];
		mod_samples[i].sample_offset = offset;
		offset += mod_samples[i].sample_len * 2;
		if (mod_samples[i].sample_len % 2) //byte-align at 4
			offset += 2;
		mod_samples[i].sample_finetune = tmp[24];
		mod_samples[i].sample_volume = tmp[25];
		mod_samples[i].sample_repeatOffset = (tmp[26] << 8) + tmp[27];
		mod_samples[i].sample_repeatLen = (tmp[28] << 8) + tmp[29];
	}

	//read song length and one unused byte
	fread(tmp, 2, 1, f);

	mod_header.mod_len = tmp[0];

	//read order
	fread(mod_order, 128, 1, f);

	mod_header.mod_patterns = 0;

	//compute number of patterns
	for (int i=0;i<mod_header.mod_len;i++)
		if (mod_order[i] >= mod_header.mod_patterns)
			mod_header.mod_patterns = mod_order[i] + 1;

	//jump over signature "M.K."
	fread(tmp, 4, 1, f);


	int nrp = mod_header.mod_channels * 64 * mod_header.mod_patterns;
	mod_patterns = new TModPattern[nrp];
	fread(tmp, nrp*4, 1, f);

// 		+-------------------------------------+
// 		|  Byte 0   Byte 1   Byte 2   Byte 3  |
// 		|-------------------------------------|
// 		| aaaaBBBB CCCCCCCC DDDDeeee FFFFFFFF |
// 		+-------------------------------------+
// 		aaaaDDDD     = sample number
// 		BBBBCCCCCCCC = sample period value
// 		eeee         = effect number
// 		FFFFFFFF     = effect parameters

	for (int i=0;i<nrp;i++)
	{
		mod_patterns[i].pat_sampleID = (tmp[i*4 + 0] & 0xF0) | (tmp[i*4 + 2] >> 4);
		mod_patterns[i].pat_effect = tmp[i*4 + 2] & 0x0F;
		mod_patterns[i].pat_paramX = tmp[i*4 + 3] >> 4;
		mod_patterns[i].pat_paramY = tmp[i*4 + 3] & 0x0F;

		u16 period = ((tmp[i*4 + 0] & 0x0F)<<8) | tmp[i*4 + 1];
		if (period == 0)
		{
			mod_patterns[i].pat_sampleNote = 0;
		}
		else
		{
			mod_patterns[i].pat_sampleNote = 100; //some number
			for (int j=0;j<36;j++)
			{
				if ((amiga_table[0][j]-2<=period) && (period <= amiga_table[0][j]+2))
				{
					mod_patterns[i].pat_sampleNote = j;
					break;
				}
			}
			if (mod_patterns[i].pat_sampleNote == 100)
			{
				printf("Sample period (%d) not found\n", period);
				return false;
			}
			}
	}

	for (int i=0;i<mod_header.mod_samples;i++)
	{
		if (mod_samples[i].sample_len)
		{
			mod_sampleData[i] = new s8[mod_samples[i].sample_len * 2];
			fread(mod_sampleData[i], mod_samples[i].sample_len * 2, 1, f);
		}
	}

	fclose(f);
	return true;
}

bool saveMod(char *fname)
{
	FILE *f = fopen(fname, "wb");
	if (!f)
		return false;

	fwrite(&mod_header, sizeof(TModHeader), 1, f);

	fwrite(mod_samples, sizeof(TModSample) * mod_header.mod_samples, 1, f);

	fwrite(mod_order, mod_header.mod_len, 1, f);

	int nrp = mod_header.mod_channels * 64 * mod_header.mod_patterns;

	fwrite(mod_patterns, nrp * sizeof(TModPattern), 1, f);

	u16 zero = 0;
	for (int i=0;i<mod_header.mod_samples;i++)
	{
		if (mod_samples[i].sample_len)
		{
			fwrite(mod_sampleData[i], mod_samples[i].sample_len *2, 1, f);
			if (mod_samples[i].sample_len % 2) //must byte-align at 4
				fwrite(&zero, 2, 1, f);
		}
	}

	fclose(f);
	return true;
}

int main(int argc, char **argv)
{
/*	if (argc != 3)
	{
		printf("Usage: makemod.exe <song.mod> <song.smd>\n");
		return -1;
	}*/

	loadMod("test.mod");
	saveMod("test.smd");

	return 0;
}